package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
	"github.com/opentracing/opentracing-go"
	zipkintracer "github.com/openzipkin-contrib/zipkin-go-opentracing"
	"github.com/openzipkin/zipkin-go"
	"github.com/openzipkin/zipkin-go/reporter/http"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"io/ioutil"
	"log"
	"net"

	pd "zt/go-study/grpc/proto"
)

var _ pd.SearchServiceServer = (*SearchService)(nil)

type SearchService struct {
	pd.UnimplementedSearchServiceServer
}

func (s *SearchService) Search(ctx context.Context, r *pd.SearchRequest) (*pd.SearchResponse, error) {
	return &pd.SearchResponse{Response: r.GetRequest() + " Server"}, nil
}

const (
	PORT = "9001"

	ServiceName = "simple_zipkin_server"
	// ZipkinHTTPEndpoint 上报到zipkin中的链路
	ZipkinHTTPEndpoint     = "http://127.0.0.1:9411/api/v2/spans"
	ZipkinRecorderHostPort = "localhost:9002"
)

func main() {
	reporter := http.NewReporter(ZipkinHTTPEndpoint)
	defer reporter.Close()

	endpoint, err := zipkin.NewEndpoint(ServiceName, ZipkinRecorderHostPort)
	if err != nil {
		log.Fatalf("zipkin.NewEndpoint err: %v", err)
	}

	trace, err := zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(endpoint))
	if err != nil {
		log.Fatalf("zipkin.NewTracer err: %v", err)
	}

	// 接入opentracing
	t := zipkintracer.Wrap(trace)
	opentracing.SetGlobalTracer(t)

	certFile := "/Users/zhoutao/project/go-study/grpc/conf/server/server.pem"
	keyFile := "/Users/zhoutao/project/go-study/grpc/conf/server/server.key"
	caFile := "/Users/zhoutao/project/go-study/grpc/conf/ca.pem"
	c, err := GetTLSCredentialsByCA(certFile, keyFile, caFile)
	if err != nil {
		log.Fatalf("GetTLSCredentialsByCA err: %v", err)
	}

	server := grpc.NewServer(
		grpc.Creds(c),
		grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(otgrpc.OpenTracingServerInterceptor(t, otgrpc.LogPayloads()))),
	)

	pd.RegisterSearchServiceServer(server, &SearchService{})

	lis, err := net.Listen("tcp", ":"+PORT)
	if err != nil {
		log.Fatalf("net.Listen err: %v", err)
	}

	server.Serve(lis)
}

// GetTLSCredentialsByCA 通过ca获取tls证书
func GetTLSCredentialsByCA(certFile, keyFile, caFile string) (credentials.TransportCredentials, error) {
	// 从证书相关文件中读取和解析信息，得到证书公钥、密钥对
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		log.Fatalf("credentials.NewServerTLSFromFile err: %v", err)
		return nil, err
	}

	// 创建一个新的、空的 CertPool
	certPool := x509.NewCertPool()
	ca, err := ioutil.ReadFile(caFile)
	if err != nil {
		log.Fatalf("ioutil.ReadFile err: %v", err)
		return nil, err
	}

	// 尝试解析所传入的 PEM 编码的证书。如果解析成功会将其加到 CertPool 中，便于后面的使用
	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		log.Fatalf("certPool.AppendCertsFromPEM err")
		return nil, err
	}

	// 构建基于 TLS 的 TransportCredentials 选项
	c := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cert},        // 设置证书链，允许包含一个或多个
		ClientAuth:   tls.RequireAndVerifyClientCert, // 要求必须校验客户端的证书
		ClientCAs:    certPool,                       // 设置根证书的集合，校验方式使用 ClientAuth 中设定的模式
	})

	return c, nil
}
